#include <string>
#include <cstddef>
#include <stdexcept>
#include <iostream>
#include <cstddef>
#include <regex>

#include "controllers/ControladorUsuario.h"
#include "controllers/ControladorRecurso.h"
#include "types/DateTime.h"
#include "models/Usuario.h"
#include "models/Carpeta.h"
#include "models/Colaborador.h"
#include "models/Archivo.h"

ControladorUsuario::ControladorUsuario() : usuariosSistema(){
	this->usr = NULL;
}

bool ControladorUsuario::ingresarNickName(string nickName){

	set<Usuario*>::iterator it;
	bool crearUsuario = true;
	validarAlphaNumericos(nickName);

	for ( it = this->usuariosSistema.begin(); it != this->usuariosSistema.end(); ++it) {
		Usuario * u = (*it);
		if ( u->noEnUso(nickName) ){
			crearUsuario = true;
		}else{
			crearUsuario = false;
		}
	}

	if (crearUsuario){
		Usuario * nuevo = new Usuario(nickName);
		this->usr = nuevo;

		this->usuariosSistema.insert(this->usr);

		return crearUsuario;
	}else{
		return this->cancelarAltaUsuario();
	}


};

bool ControladorUsuario::cancelarAltaUsuario(){
	return false;
};

void ControladorUsuario::ingresarDatosUsuario(string sexo, DateTime fechaNacimiento){
	this->usr->setSexo(sexo);
	this->usr->setFechaNacimiento(fechaNacimiento);
	this->usr->setEdad();
};

void ControladorUsuario::elegirUsuarioSistema(string nickName){
    set<Usuario*>::iterator it = this->usuariosSistema.begin();
    bool ok=false;
    Usuario* u;
    while ((!ok)&&(it != this->usuariosSistema.end())){
        u = (*it);
        if (u->noEnUso(nickName)==0)
            ok = true;
        ++it;
    }
    if (!ok){
        throw std::out_of_range("El usuario elegido no existe en el sistema.\n");
    }else {
        cout << "Nombre: ";
        u->imprimirNombreUsuario();
        cout << " Sexo: ";
        u->imprimirSexoUsuario();
        cout << " Edad: ";
        u->imprimirEdadUsuario();
        cout << " Fecha de nacimiento: ";
        u->imprimirFechaNacUsuario();
        cout << endl << "Informacion de recursos propios:" << endl;
        vector<string*> v1 = u->ObtenerInfoRecursosPropios();
        unsigned sz1 = v1.size();
        for (unsigned i=0; i<sz1; i++){
            cout << v1[i] << "\n";
        }
        cout << endl << "Informacion de recursos colaborados:\n" << endl;
        vector<string*> v2 = u->ObtenerInfoRecursosColaborador(nickName);
        unsigned sz2 = v2.size();
        for (unsigned j=0; j<sz2; j++){
            cout << v2[j] << "\n";
        }
    }
};

void ControladorUsuario::verInformacionUsuario(){
    imprimirUsuariosSistema();
};

bool ControladorUsuario::elegirAutorModificacionArchivo(string modificador){

	bool modificar, seguir = true;
	set<Usuario*>::iterator it = this->usuariosSistema.begin();
	while (it != this->usuariosSistema.end()  && (seguir == true)) {
		Usuario * u = (*it);

		if ( u->getNickname().compare(modificador) == 0 ){
			modificar = true;
			seguir = false;
		}else{
			modificar = false;
		}
		++it;
	}

	return modificar;

}

void ControladorUsuario::encontrarUsuario(string nickname)
{};

bool ControladorUsuario::elegirUsuarioColaborador(IControladorRecurso * iCR, string nickName){

	bool registrar = false;
	bool seguir = true;
	set<Usuario*>::iterator it =this->usuariosSistema.begin();
	while (it != this->usuariosSistema.end()  && (seguir == true)) {

		if ( (*it)->getNickname().compare(nickName) == 0 ){

			registrar = true;
			this->usr = (*it);
			seguir = false;

		}else{
			registrar = false;
		}

		++it;
	}

	if (registrar){
		ControladorRecurso * resourcesController = (ControladorRecurso*)iCR;
		Carpeta* c = resourcesController->getCarpetaAColaborar();
		this->usr->getCarpetasColaboradas().insert(c);

		return registrar;
	}else{
		return registrar;
	}
}


bool ControladorUsuario::registrarUsuarioColaborador(string nickName ){

	bool registrar = false;
	bool seguir = true;

	set<Usuario*>::iterator it =this->usuariosSistema.begin();
	while (it != this->usuariosSistema.end()  && (seguir == true)) {

		this->usr = (*it);

		if ( (*it)->getNickname().compare(nickName) == 0 ){

			registrar = true;
			this->usr = (*it);
			seguir = false;

		}else{
			registrar = false;
		}

		++it;
	}

	if (registrar == true){
		set<Recurso*>::iterator itPropios;
		set<Recurso*> propios;
		set<Carpeta*>::iterator itColaboradas;
		set<Carpeta*> colaboradas;

		// Si existe imprimo las carpetas del usuario:
		cout << "Recursos propios:" << endl;
		propios = this->usr->getRecursosPropios();
		for (itPropios = this->usr->getRecursosPropios().begin(); itPropios != propios.end(); ++itPropios) {
					Recurso* r = (*itPropios);
					cout << "Nombre: " << r->getNombre();
					cout << " Ruta: " << r->getRuta()  << endl;
		}

		colaboradas = this->usr->getCarpetasColaboradas();
		for (itColaboradas = colaboradas.begin(); itColaboradas != colaboradas.end(); ++itPropios) {
			Carpeta* a = (Carpeta*)(*itColaboradas);
			cout << "Nombre: " << a->getNombre();
			cout << " Ruta: " << a->getRuta()  << endl;
		}

		return registrar;

	}else{
		return registrar;
	}
}

bool ControladorUsuario::registrarUsuarioColaboradorRaiz(IControladorRecurso * iCR, string nickName ){

	bool registrar = false;
	bool seguir = true;

	set<Usuario*>::iterator it =this->usuariosSistema.begin();
	while (it != this->usuariosSistema.end()  && (seguir == true)) {

		this->usr = (*it);

		if ( (*it)->getNickname().compare(nickName) == 0 ){

			registrar = true;
			this->usr = (*it);
			seguir = false;

		}else{
			registrar = false;
		}

		++it;
	}

	if (registrar == true){

		ControladorRecurso * resourcesController = (ControladorRecurso*)iCR;

		Carpeta* carpeta = resourcesController->setCarpetaAColaborar("raiz", "raiz/");

		// Inicializa al colaborador en la raiz
		Colaborador* c = new Colaborador();
		c->setCarpeta(carpeta->getNombre() + "," + carpeta->getRuta());
		c->setUsuario(nickName);
		c->setFechaAgregado(DateTime());

		// Inserta al usuario de nickName 'nickName' en la lista de colaboradores de la carpeta 'raiz'
		carpeta->obtenerColaboradoresCarpeta().insert(c);

		// Agrega la carpeta a la lista de carpetas colaboradas por el usuario
		this->usr->getCarpetasColaboradas().insert(carpeta);

		return registrar;
	}else{
		return registrar;
	}
}

bool ControladorUsuario::esColaborador(string nickname){
    set<Carpeta*>::iterator itColaboradas;
	set<Carpeta*> colaboradas;

    this->usr = obtenerUsuarioDelSistema(nickname);

    colaboradas = this->usr->getCarpetasColaboradas();


   // cout << "colaboradas: " << colaboradas.size() << endl;
   // cout << "this->usr->getCarpetasColaboradas(): " <<this->usr->getCarpetasColaboradas().size() << endl;
	itColaboradas = colaboradas.begin();
	while (  itColaboradas != colaboradas.end()) {
        Carpeta* a = (*itColaboradas);
        if ((a->getNombre().compare("raiz") && a->getRuta().compare("raiz/"))){
			return true;
        }
		++itColaboradas;
    }

    return false;
}

void ControladorUsuario::elegirUsuario(string nickName){

	set<Usuario*>::iterator it;
	for ( it = this->usuariosSistema.begin(); it != this->usuariosSistema.end(); ++it) {
	       this->usr = (*it);
	}
};

Usuario* ControladorUsuario::obtenerUsuarioDelSistema(string nickName){

	set<Usuario*>::iterator it;
	for ( it = this->usuariosSistema.begin(); it != this->usuariosSistema.end(); ++it) {
		Usuario* u = (*it);
		if (u->getNickname().compare(nickName) == 0){
			this->usr = (*it);
		}
	}

	return this->usr;
}

void ControladorUsuario::imprimirInfoRecursos()
{};

void ControladorUsuario::imprimirNicknamesUsuarios(){
	set<Usuario*>::iterator it;
		 for ( it = this->usuariosSistema.begin(); it != this->usuariosSistema.end(); ++it) {
			  Usuario * u = (*it);
			  u->imprimirNombreUsuario();
		 }
};

void ControladorUsuario::imprimirUsuariosSistema(){
    set<Usuario*>::iterator it;
    for ( it = this->usuariosSistema.begin(); it != this->usuariosSistema.end(); ++it) {
        Usuario * u = (*it);
        cout << "Nombre: ";
        u->imprimirNombreUsuario();
        cout << " Fecha de nacimiento: ";
        u->imprimirFechaNacUsuario();
        cout << " Sexo: ";
        u->imprimirSexoUsuario();
        cout << " Edad: ";
        u->imprimirEdadUsuario();
        cout << endl;
    }
}

/* Notifica a todos los usuarios que tengan relacion con el archivo modificado */
void ControladorUsuario::notificarUsuarios(IControladorRecurso * iCR, string accion){

	set<Usuario*>::iterator it;
	set<Recurso*> propios;

	// Obtengo el recurso actual apuntado en la controladora recurso. Recientemente modificado.
	ControladorRecurso * resourcesController = (ControladorRecurso*)iCR;

	Archivo* recursoActual = (Archivo*)resourcesController->getRecurso();
	string nombreArchivo = recursoActual->getNombre();
	string rutaArchivo = recursoActual->getRuta();

	/* Recorre todos los usuarios del sistema verificando si para el archivo modificado
	 * se debe notificar al usuario
	 */
	for ( it = this->usuariosSistema.begin(); it != this->usuariosSistema.end(); ++it) {

		Usuario* u = (*it);
		propios = u->getRecursosPropios();
		set<Recurso*>::iterator itPropios;
		for ( itPropios = propios.begin(); itPropios != propios.end(); ++itPropios) {
			Archivo* a = (Archivo*)(*itPropios);
			if (a->getNombre().compare(nombreArchivo) && a->getRuta().compare(rutaArchivo)){
				//u->notificar(accion, a->getNombre());
				a->modificar(accion);
			}
		}
	}
}

bool ControladorUsuario::esPrefijoRuta(string ruta1, string ruta2){
	return	(ruta1.find(ruta2) != string::npos);
}


bool ControladorUsuario::permisosModificacion(string modificador, string nombre, string ubicacion){

	set<Recurso*>::iterator itPropios;
	set<Carpeta*>::iterator itColaboradas;
	set<Recurso*> propios;
	set<Carpeta*> colaboradas;

	bool tienePermisosArchivo, tienePermisosCarpeta = false;
	bool encontrado, seguir = false;

	set<Usuario*>::iterator it =this->usuariosSistema.begin();
	while (it != this->usuariosSistema.end() && (!encontrado)) {
		if ( (*it)->getNickname().compare(modificador) == 0 ){
			encontrado = true;
			this->usr = (*it);
		}
		++it;
	}

	if (encontrado){
		propios = this->usr->getRecursosPropios();
		itPropios = propios.begin();
		seguir = true;
		while (itPropios != propios.end() && (seguir)) {
			Archivo* a = (Archivo*)(*itPropios);
			if (a->getNombre().compare(nombre) && a->getRuta().compare(ubicacion)){
				tienePermisosArchivo = true;
				seguir = false;
			}
			++itPropios;
		}

		if (seguir){
			colaboradas = this->usr->getCarpetasColaboradas();
			itColaboradas = colaboradas.begin();
			seguir = true;
			while (  itColaboradas != colaboradas.end() && (seguir)) {
				Carpeta* a = (*itColaboradas);
				if ((a->getNombre().compare(nombre) && a->getRuta().compare(ubicacion) ) ||
					(esPrefijoRuta(a->getRuta(), ubicacion))	){
					tienePermisosCarpeta = true;
					seguir = false;
				}
				++itColaboradas;
			}
		}

		return (tienePermisosArchivo || tienePermisosCarpeta);

	}else{
		return encontrado;
	}

}

void ControladorUsuario::esUsuarioDelSistema(string nickName){
	set<Usuario*>::iterator it;
	bool repetido = false;

	if(!this->usuariosSistema.empty()){
		 for ( it = this->usuariosSistema.begin(); it != this->usuariosSistema.end(); ++it) {
			  Usuario * u = (*it);
			  string nombre = u->getNickname();
			  if (nombre.compare(nickName) == 0){
				  repetido = true;
			  }

		 }

	}

	if(!repetido)
		throw std::out_of_range("No es usuario del sistema");
};

void ControladorUsuario::noHayUsuarios(){

	if(this->usuariosSistema.empty()){
		throw std::out_of_range("No existen usuarios");

	}


};

void ControladorUsuario::validarAlphaNumericos(string palabra){

	std::regex r("[a-zA-Z0-9]+");  // sola letras y numeros
    bool resultado = std::regex_match(palabra, r);
	if(!resultado)
		throw std::out_of_range("Solo se permiten letras y numeros.");


}

void ControladorUsuario::setInterfazControladorRecurso(IControladorRecurso* CR){
	this->CR=(ControladorRecurso*)CR;
}